package com.ohuang.router_compiler;

import com.google.auto.service.AutoService;
import com.google.common.reflect.TypeToken;

import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;

@AutoService(Processor.class)
public class RouteProcessor extends AbstractProcessor {
    private Types typeUtils;
    private Messager messager;
    private Filer filer;
    private Elements elementUtils;
    private Map<String, Map<String, String>> map = new LinkedHashMap<>();
    private Map<String, String> mapUtil = new LinkedHashMap<>();

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        typeUtils = processingEnvironment.getTypeUtils();
        messager = processingEnvironment.getMessager();
        filer = processingEnvironment.getFiler();
        elementUtils = processingEnvironment.getElementUtils();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotations = new LinkedHashSet<>();
        annotations.add(Router.class.getCanonicalName());
        return annotations;
    }


    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {


        for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(Router.class)) {

            TypeElement typeElement = (TypeElement) annotatedElement;
            Router route = typeElement.getAnnotation(Router.class);
            String path = route.path();


            String s = typeElement.getQualifiedName().toString();

            String[] b = path.split("/");
            if (b.length > 1) {
                if (b[0].isEmpty()) {
                    throw new OhRouterException("OhRouter path 必须要有两级 如@Router(path=main/Main)" + s);
                } else {
                    if (map.containsKey(b[0])) {
                        Map<String, String> map2 = map.get(b[0]);
                        map2.put(path, s);
                    } else {
                        Map<String, String> map2 = new LinkedHashMap<>();
                        map2.put(path, s);
                        map.put(b[0], map2);
                    }

                }
            } else {
                throw new OhRouterException("OhRouter path 必须要有两级 如@Router(path=main/Main)" + s);
            }
        }
        try {
            String pageName = "com.ohuang.router";
            Set<String> set1 = map.keySet();

            for (String s : set1) {
                String s1 = "RouterMap$" + s;
                createClass(pageName, s1, map.get(s), elementUtils, filer);
                mapUtil.put(s, pageName + "." + s1);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }


        return true;
    }


    private void info(String msg, Object... args) {
        messager.printMessage(Diagnostic.Kind.NOTE, String.format(msg, args));
    }

    public void createClass(String pageName, String className, Map<String, String> map, Elements elementUtils, Filer filer) throws IOException {

        MethodSpec.Builder method = MethodSpec.methodBuilder("init")
                .addModifiers(Modifier.PUBLIC)
                .addStatement("map =new $T()", HashMap.class);
        Set<String> set = map.keySet();
        for (String s : set) {
            String c = map.get(s);
            method.addStatement("map.put($S, "+c+".class )", s);
        }


        MethodSpec.Builder method2 = MethodSpec.methodBuilder("get")
                .addModifiers(Modifier.PUBLIC)
                .addParameter(String.class, "name")
                .returns(Class.class)
                .addStatement("Class s=null")
                .beginControlFlow("if(map!=null)")
                .addStatement("s=map.get(name)")
                .endControlFlow()
                .addStatement("return s");

        TypeSpec typeSpec = TypeSpec
                .classBuilder(className)
                .addModifiers(Modifier.PUBLIC)
                .addMethod(method.build())
                .addMethod(method2.build())
                .addField(new TypeToken<Map<String, Class>>() {
                }.getType(), "map", Modifier.PUBLIC)
                .addSuperinterface(RouterInterface.class)
                .build();
        JavaFile.builder(pageName, typeSpec).build().writeTo(filer);
    }


}
